package FlowGraph;

import antlr.collections.AST;
import java.util.*;
import java.io.*;

import SymbolTable.*;
import FlowGraph.Nodes.BasicBlockNode;
import FlowGraph.SSA.*;
import FlowGraph.StatementInfo.StatementInfo;
import util.Line;

//////////////////////////////////////////////////////////////////////////
//// FlowGraphNode
/**

@author Ivan Jeukens
@version v 0.1 03/10/2001
*/
public class FlowGraphNode {

    public static final int UNDEF = 0;
    public static final int BASIC_BLOCK = 1;
    public static final int METHOD_CALL = 2;
    public static final int IF_BEGIN = 3;
    public static final int IF_END = 4;
    public static final int SWITCH_BEGIN = 5;
    public static final int SWITCH_END = 6;
    public static final int CONTINUE = 7;
    public static final int BREAK = 8;
    public static final int WHILE_BEGIN = 9;
    public static final int WHILE_END = 10;
    public static final int FOR_BEGIN = 11;
    public static final int FOR_END = 12;
    public static final int DO_BEGIN = 13;
    public static final int DO_END = 14;
    public static final int DO_CONDITION = 15;
    public static final int RETURN = 16;
    public static final int SOURCE = 17;
    public static final int SINK = 18;
    public static final int THROW = 19;
    public static final int TRY_BEGIN = 20;
    public static final int TRY_END = 21;
    public static final int CATCH_BEGIN = 22;
    public static final int CATCH_END = 23;
    public static final int FINALLY_BEGIN = 24;
    public static final int FINALLY_END = 25;
    public static final int SYNC_BEGIN = 26;
    public static final int SYNC_END = 27;
    public static final int INNER_CLASS = 28;

    public FlowGraphNode(AST absTreeNode, int type) {
        _absTreeNode = absTreeNode;
        _type = type;

        _inputEdges = new Vector();
        _outputEdges = new Vector();

        _variables = null;
        _dominanceFrontier = new LinkedList();

        _phiNodes = new LinkedList();
        _definedVars = new HashMap();
    }

    public FlowGraphNode(AST absTreeNode, int type, int inputCap,
            int outputCap) {
        _absTreeNode = absTreeNode;
        _type = type;

        _inputEdges = new Vector(inputCap);
        _outputEdges = new Vector(outputCap);

        _variables = null;
        _dominanceFrontier = new LinkedList();

        _phiNodes = new LinkedList();
        _definedVars = new HashMap();
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public methods                     ////

    // Retorna os nos de output desse fgnode, que nao sao alcancados  a partir
    // de arestas de exception e back edges
    public static Iterator nonExceptionBackOutputNodes(FlowGraphNode node) {
        LinkedList ret = new LinkedList();
        Iterator oute = node.outputEdgesIterator();
        while(oute.hasNext()) {
            FlowGraphEdge edge = (FlowGraphEdge) oute.next();
            if(edge.isException() || edge.isBack()) continue;
            ret.add(edge.getTo());
        }
        return ret.iterator();
    }
    
    public void setContainer(FlowGraph fg) {
        _container = fg;
    }

    public FlowGraph getContainer() {
        return _container;
    }

    public void addInputEdge(FlowGraphEdge edge) {
        _inputEdges.add(edge);
    }

    public void addOutputEdge(FlowGraphEdge edge) {
        _outputEdges.add(edge);
    }    

    public FlowGraphEdge getInputEdge(int index) {
        return (FlowGraphEdge) _inputEdges.get(index);
    }

    public FlowGraphNode getInputNode(int index) {
        FlowGraphEdge edge = (FlowGraphEdge) _inputEdges.get(index);
        return edge.getFrom();
    }

    public int inputSize() {
        return _inputEdges.size();
    }

    public Iterator inputEdgesIterator() {
        return _inputEdges.iterator();
    }

    public int getInputIndex(FlowGraphNode node) {
        for(int i = 0;i < _inputEdges.size();i++) {
            FlowGraphEdge edge = (FlowGraphEdge) _inputEdges.get(i);
            if(edge.getFrom() == node) {
                return i;
            }
        }
        return -1;
    }

    public FlowGraphNode getOutputNode(int index) {
        FlowGraphEdge edge = (FlowGraphEdge) _outputEdges.get(index);
        return edge.getTo();
    }

    public FlowGraphEdge getOutputEdge(int index) {
        return (FlowGraphEdge) _outputEdges.get(index);
    }

    public FlowGraphEdge getOutputEdge(FlowGraphNode node) {
        for(int i = 0;i < _outputEdges.size();i++) {
            FlowGraphEdge edge = (FlowGraphEdge) _outputEdges.get(i);
            if(edge.getTo() == node) return edge;
        }
        return null;
    }

    public int outputSize() {
        return _outputEdges.size();
    }
    
    public Iterator outputEdgesIterator() {
        return _outputEdges.iterator();
    }
    
    public FlowGraphEdge removeOutputEdge(FlowGraphNode to) {
        for(int i = 0;i < _outputEdges.size();i++) {            
            FlowGraphEdge edge = (FlowGraphEdge) _outputEdges.get(i);
            if(edge.getTo() == to) {
                _outputEdges.remove(i);
                return edge;
            }
        }
        return null;
    }
    
    public FlowGraphEdge removeInputEdge(FlowGraphNode from) {
        for(int i = 0;i < _inputEdges.size();i++) {            
            FlowGraphEdge edge = (FlowGraphEdge) _inputEdges.get(i);
            if(edge.getFrom() == from) {
                _inputEdges.remove(i);
                return edge;
            }
        }
        return null;
    }

    public void removeOutputEdge(FlowGraphEdge edge) {
        _outputEdges.remove(edge);
    }

    public void removeInputEdge(FlowGraphEdge edge) {
        _inputEdges.remove(edge);
    }

    public void replaceInputConnection(FlowGraphNode from, FlowGraphNode newfrom) {
        for(int i = 0;i < _inputEdges.size();i++) {
            FlowGraphEdge edge = (FlowGraphEdge) _inputEdges.get(i);
            if(edge.getFrom() == from) {
                from.removeOutputEdge(edge);
                edge.setFrom(newfrom);
                newfrom.addOutputEdge(edge);
            }
        }
    }
    
    public void replaceOutputConnection(FlowGraphNode to, FlowGraphNode newto) {
        for(int i = 0;i < _outputEdges.size();i++) {
            FlowGraphEdge edge = (FlowGraphEdge) _outputEdges.get(i);
            if(edge.getTo() == to) {
                to.removeInputEdge(edge);
                edge.setTo(newto);
                newto.addInputEdge(edge);
            }
        }
    }
    
    public boolean hasOutput(FlowGraphNode node) {
        for(int i = 0;i < _outputEdges.size();i++) {
            FlowGraphEdge edge = (FlowGraphEdge) _outputEdges.get(i);
            if(edge.getTo() == node) return true;
        }
        return false;
    }        

    public AST getAbsTreeNode() {
        return _absTreeNode;
    }

    public int getType() {
        return _type;
    }

    public void setSymbolTable(SymbolTable sym, boolean vardef) {
        _variables = sym;
        _hasVarDef = vardef;
        if(vardef) {
            _variables.setOwner(this);
        }
    }

    public SymbolTable getSymbolTable() {
        return _variables;
    }

    public Iterator symbolTableIterator() {
        return _variables.iterator();
    }
    
    public void setScope(int number) {
        _scopeNumber = number;
    }
    
    public int getScope() {
        return _scopeNumber;
    }

    public String toString() {
        if(_type == SOURCE) {
            return "SOURCE";
        }
        else
        if(_type == SINK) {
            return "SINK";
        }
        else
        if(_type == UNDEF) {
            return "??UNDEF??";
        }

        if(_absTreeNode == null) return "???";

        return _absTreeNode.toStringTree();
    }

    public void setVarDef(boolean value) {
        _hasVarDef = value;   
    }

    public boolean hasVarDef() {
        return _hasVarDef;   
    }
    
    public void setUniqueNum(int v) {
        _uniqueNum = v;
    }
    
    public int getUniqueNum() {
        return _uniqueNum;
    }
    
    public void setLabel(String label) {
        _label = label;
    }
        
    public String getLabel() {
        return _label;
    }

    public void openScope() {
        _openedScopes++;
    }
    
    public void closeScope() {
        _closedScopes++;
    }
    
    public int getOpenedScopes() {
        return _openedScopes;
    }
    
    public int getClosedScopes() {
        return _closedScopes;
    }
    
    public void setOpenedScopes(int value) {
        _openedScopes = value;
    }
    
    public void setClosedScopes(int value) {
        _closedScopes = value;
    }
    
    public boolean hasVar(STEntry entry) {
        return (_variables.find(entry.getName()) != null);
    }
    
    public STEntry getVar(String name) {
        return (STEntry)_variables.find(name);
    }

    public void setDominatorTreeNode(DominatorTreeNode node) {
        _dominatorTreeNode = node;
    }
    
    public DominatorTreeNode getDominatorTreeNode() {
        return _dominatorTreeNode;
    }
    
    public Iterator dominanceFrontierIterator() {
        return _dominanceFrontier.iterator();
    }
    
    public void addToDominanceFrontier(FlowGraphNode node) {
        _dominanceFrontier.add(node);
    }
    
    public void addPhiNode(PhiNode node) {
        _phiNodes.add(node);
    }
    
    public Iterator phiNodeIterator() {
        return _phiNodes.iterator();
    }
    
    public int phiNodeSize() {
        return _phiNodes.size();
    }
    
    public PhiNode getPhiNode(int pos) {
        return (PhiNode) _phiNodes.get(pos);
    }
    
    public void removePhiNode(PhiNode node) {
        _phiNodes.remove(node);
    }

    public boolean containsPhiNode(VariableInfo info) {
        for(int i = 0;i < _phiNodes.size();i++) {
            PhiNode phi = (PhiNode) _phiNodes.get(i);
            if(phi.getVariableInfo() == info) return true;
        }
        return false;
    }

    public void addDefinedVar(STEntry entry) {
        _definedVars.put(entry, null);
    }

    public boolean definesVar(STEntry entry) {
        return _definedVars.containsKey(entry);
    }

    public String typeToString() {
        switch(_type) { 
            case UNDEF: return "UNDEF";
            case BASIC_BLOCK: return "BB";
            case METHOD_CALL: return "MC";
            case IF_BEGIN: return "IFB";
            case IF_END: return "IFE";
            case SWITCH_BEGIN: return "SWB";
            case SWITCH_END: return "SWE";
            case CONTINUE: return "CONT";
            case BREAK: return "BREAK";
            case WHILE_BEGIN: return "WLB";
            case WHILE_END: return "WLE";
            case FOR_BEGIN: return "FORB";
            case FOR_END: return "FORE";
            case DO_BEGIN: return "DOB";
            case DO_END: return "DOE";
            case DO_CONDITION: return "DOCOND";
            case RETURN: return "RET";
            case SOURCE: return "SOURCE";
            case SINK: return "SINK";
            case THROW: return "THROW";
            case TRY_BEGIN: return "TRYB";
            case TRY_END: return "TRYE";
            case CATCH_BEGIN: return "CATCHB";
            case CATCH_END: return "CATCHE";
            case FINALLY_BEGIN: return "FINB";
            case FINALLY_END: return "FINE";
            case SYNC_BEGIN: return "SYNCB";
            case SYNC_END: return "SYNCE";
            case INNER_CLASS: return "INNERC";
        }
        return "";
    }
   
    public void debug(DataOutputStream outs) {
        try {
            outs.writeInt(hashCode());
            if(_type == BASIC_BLOCK) {
                BasicBlockNode bb = (BasicBlockNode) this;
                outs.writeUTF(bb.toString());
            }
            else
            if(_type == METHOD_CALL) {
                outs.writeUTF(toString());
            }
            else
            if(_absTreeNode != null) {
                outs.writeUTF(_absTreeNode.toStringTree());
            }
            else {
                outs.writeUTF("");
            }
            outs.writeUTF((new Integer(_scopeNumber)).toString());
            outs.writeUTF((new Boolean(_hasVarDef)).toString());
            outs.writeUTF((new Integer(_openedScopes)).toString());
            outs.writeUTF((new Integer(_closedScopes)).toString());

            if(_variables == null) {
                System.out.println("Olha o nul...");
                System.out.println("Type == " + typeToString());
                System.out.println(_absTreeNode.toStringTree());
            }

            outs.writeUTF(_variables.toString());

            if(_dominatorTreeNode == null) {
                outs.writeInt(0);
            }
            else {
                outs.writeInt(_dominatorTreeNode.childrenSize());
                Iterator ite = _dominatorTreeNode.childrenIterator();
                while(ite.hasNext()) {
                    DominatorTreeNode dmnode = (DominatorTreeNode) ite.next();
                    FlowGraphNode container = dmnode.getContainer();
                    outs.writeInt(container.hashCode());
                }
            }

            outs.writeInt(_dominanceFrontier.size());
            for(int i = 0;i < _dominanceFrontier.size();i++) {
                FlowGraphNode node = (FlowGraphNode) _dominanceFrontier.get(i);
                outs.writeInt(node.hashCode());
            }
            
            outs.writeInt(_phiNodes.size());
            for(int i = 0;i < _phiNodes.size();i++) {
                PhiNode phi = (PhiNode) _phiNodes.get(i);
                int inputSize = phi.inputSize();
                VariableInfo info = phi.getVariableInfo();
                String varname = info.getSTEntry().getName();
                
                StringBuffer buf = new StringBuffer(phi.getDefLabel() + " = ");
                for(int k = 0;k < inputSize;k++) {
                    buf.append(phi.getLabel(k) + " ");
                }
                buf.append("\n");
                outs.writeUTF(buf.toString());
            }

            outs.writeUTF("Is marked SDF ? = " + isSDFMarked());
        }
        catch(IOException ex) {
            System.out.println(ex);
        }
    }

    public StatementInfo getStatementInfo() {
        return null;
    }

    public boolean isDeleted() {
        return _deleted;
    }

    public void setDeleted() {
        _deleted = true;
    }

    public Line getLine() {
        return null;
    }
    
    public void markSDF() {
        _markSDF = true;
    }
    
    public boolean isSDFMarked() {
        return _markSDF;
    }
    
    ///////////////////////////////////////////////////////////////////
    ////                      protected variables                  ////

    protected Vector _inputEdges;
    protected Vector _outputEdges;

    protected int _type = UNDEF;

    protected AST _absTreeNode;
    protected SymbolTable _variables;

    protected int _scopeNumber;
    protected boolean _hasVarDef;
    protected int _uniqueNum;

    protected int _openedScopes;
    protected int _closedScopes;

    protected String _label;

    protected FlowGraph _container;

    protected DominatorTreeNode _dominatorTreeNode;
    protected LinkedList _dominanceFrontier;
    protected HashMap _definedVars;
    protected LinkedList _phiNodes;

    // Indica que esse no deveria ser removido.
    protected boolean _deleted;
    
    protected boolean _markSDF = false;
}
